1. Defining Unit Testing
Testing means different
things, depending on the context in which it's used. Developers are
usually responsible for testing the code they write — unit testing.
After code passes unit tests, it's usually checked into a source code
control repository. At some point, the entire system under development
is compiled, and quality assurance testers perform even more testing.
The kinds of tests you might encounter include
Unit tests: These programs are written by developers to test the code they write.
Integration tests: These test units of code after they're integrated with each other.
System tests: During system tests,
the entire integrated system is tested. Depending on the kind of
software being tested, system tests might include user interface
testing, regression testing, and load testing.
Acceptance tests: During acceptance tests, any or all of the system's stakeholders might participate in testing the software in a lab.
When software progresses
through testing, tests become less focused on the inner workings of the
code. As a result, testing becomes less automated and requires more user
interaction. In the case of acceptance testing, a test lab is often set
up where end users come in and bang away at the system for weeks at a
time, uncovering bugs and functional shortcomings.
As a developer, you're
actively involved in writing and running unit tests. Your interaction
with the testing process beyond unit testing depends on several factors,
including the extent to which your code provides any of the system's
core functionality.
Of course, use of unit testing
doesn't mean that no bugs exist. Rather, the implicit understanding is
that when you "check in" your code, you're telling other developers that
the code works at some basic level of functionality. Code that breaks
every time that it's called by another developer shows a pretty good
indication that you're either not unit testing at all or not doing
enough unit testing.
If you've worked as a tester,
you can likely spot those developers who never run unit tests. Not only
does their code usually not conform to the requirements, but it usually
blows up as soon you try to test it. Other developers, however, are very
conscientious about unit testing their code. As a result, you won't
need to interact with those developers as much. In other words, if you
don't want quality assurance and your fellow developers on your back,
unit test your code.
By definition, unit
testing is about writing code. When you write a unit test, you're
writing code to test code. You write a unit test to test a single unit
of your code. For example, assume that your project includes code for a Customer object and an Address
object. You should have one unit test for each object. The unit test
contains multiple tests to test the various methods and properties of
your object. Figure 1 shows an example.
You'll find tons of rules
about how you should write your tests. However, they're not really
rules: They're more like opinions. And everybody has an opinion on what
it means to unit test. Some good guidelines to follow are
All tests should be automated and run as part of a suite.
All your code should be tested.
You should write your tests before you write your code.
NOTE
Getting started with unit
testing can be quite overwhelming. Although these approaches are valid
and ones to which you should aspire, it's better to
Write imperfect tests than to write no tests at all.
Run
tests manually before you check in your code than run no tests at all.
Test the riskiest parts of your code first until you get the hang of
unit testing.
2. Unit Testing in Visual Studio
Unit testing is an
important good coding practice. While you're coding, you should write
tests that make sure your code works as you expect. Unit tests aren't
supposed to test every aspect of the system. Rather, unit tests are
sanity checks you use to make sure that your code works. For example, if
your requirement states that the function should return an integer, you
might create a unit test to test that the value returned by your
function is indeed an integer.
When used properly, unit tests help you achieve the following important goals:
Write better code.
Have a starting point for testing code.
Keep the development process flowing.
Increase your confidence in your code.
Lots of patterns and
frameworks are available for writing unit tests. At its simplest, a unit
test simply tests that your code does what it says it does. Unit tests
either pass or fail. There's no in-between.
2.1. Creating unit tests
Before you can write a unit test, you need code to test. This section uses a very simple Hello World example with the following methods:
CreateMessage(): Creates a Hello World message, depending on whether the user supplies a name.
SayHello(): Displays a Hello World message in a message box.
These methods are used in a Windows Form that displays a Hello World message. If the user enters a name in a text box on the form, the user's name appears in the message.
Listing 1 shows the code to be unit tested. Notice the two methods to test.
Listing 1. Sample Unit Test Code
private string CreateMessage(string name) { if (name == null) { throw new ArgumentNullException("name"); } else if (name == string.Empty) { throw new ArgumentException("Parameter must not be empty.", "name"); }
return ("Hello" + " " + name + "!"); }
private void SayHello(string name) { if (name == null) { throw new ArgumentNullException("name"); } else if (name == string.Empty) { throw new ArgumentException("Parameter must not be empty.", "name"); }
MessageBox.Show(CreateMessage(name), "Sample Application", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
|
To test this code, you write a set of tests that makes sure that the output is as expected. For example, the SayHello() method is supposed to return Hello World. The unit test should test the return value. Visual Studio 2010 automates this unit testing for you.
To create unit tests for these two methods:
Select the methods in the code editor.
Right-click and choose Create Unit Tests from the contextual menu, shown in Figure 2.
Visual Studio displays the Create Unit Tests dialog box, shown in Figure 3.
Click OK.
Visual
Studio creates a new Visual C# project in your solution that creates a
test harness to unit tests for the methods you selected in Figure 3. Figure 4 shows this project in the Solution Explorer.
Each test you create is a test case. Some functions might require multiple test cases. Visual Studio automatically creates unit tests for you, as shown in Listing 2.
Listing 2. Code to Test the Set of Code
namespace TestProject1 { /// <summary> ///This is a test class for Form1Test and is intended ///to contain all Form1Test Unit Tests
///</summary> [TestClass()] public class Form1Test { private TestContext testContextInstance;
/// <summary> ///Gets or sets the test context which provides ///information about and functionality for the current test run. ///</summary> public TestContext TestContext { get { return testContextInstance; } set { testContextInstance = value; } }
#region Additional test attributes // //You can use the following additional attributes as you write your tests: // //Use ClassInitialize to run code before running the first test in the class //[ClassInitialize()] //public static void MyClassInitialize(TestContext testContext) //{ //} // //Use ClassCleanup to run code after all tests in a class have run //[ClassCleanup()] //public static void MyClassCleanup() //{ //} // //Use TestInitialize to run code before running each test //[TestInitialize()] //public void MyTestInitialize() //{ //} // //Use TestCleanup to run code after each test has run //[TestCleanup()] //public void MyTestCleanup() //{ //} // #endregion
/// <summary> ///A test for CreateMessage ///</summary> [TestMethod()]
[DeploymentItem("WindowsFormsApplication1.exe")] public void CreateMessageTest() { Form1_Accessor target = new Form1_Accessor(); // TODO: Initialize to an appropriate value string name = string.Empty; // TODO: Initialize to an appropriate value string expected = string.Empty; // TODO: Initialize to an appropriate value string actual; actual = target.CreateMessage(name); Assert.AreEqual(expected, actual); Assert.Inconclusive("Verify the correctness of this test method."); }
/// <summary> ///A test for SayHello ///</summary> [TestMethod()] [DeploymentItem("WindowsFormsApplication1.exe")] public void SayHelloTest() { Form1_Accessor target = new Form1_Accessor(); // TODO: Initialize to an appropriate value string name = string.Empty; // TODO: Initialize to an appropriate value target.SayHello(name); Assert.Inconclusive("A method that does not return a value cannot be verified."); } } }
|
The Assert class in the Microsoft.VisualStudio.TestTools.UnitTesting namespace provides a set of static methods that provide you the conditions for your unit tests. Methods on the Assert class throw the AssertFailedException if the condition specified isn't true. Table 1 lists the methods on the Assert class.
Table 1. Assert Class Static Methods
Method | Description |
---|
AreEqual | Verifies that specified values are equal |
AreNotEqual | Verifies that specified values aren't equal |
AreNotSame | Verifies that specified object variables refer to different objects |
AreSame | Verifies that specified objects refer to the same object |
Fail | Fails an assertion without checking any conditions |
Inconclusive | Indicates that an assertion can't be proven true or false. Also used to indicate an assertion that hasn't yet been implemented |
IsFalse | Verifies that a specified condition is false |
IsInstanceOfType | Verifies that a specified object is an instance of a specified type |
IsNotInstanceOfType | Verifies that a specified object isn't an instance of a specified type |
IsNotNull | Verifies that a specified object isn't null |
IsNull | Verifies that a specified object is null |
IsTrue | Verifies that a specified condition is true |